export default class RequestInstance {
  http: XMLHttpRequest | undefined;
  timeout: number;
  error?: (e: RequestInstance) => void;
  ok?: (e: RequestInstance) => void;
  cancel?: (e: RequestInstance) => void;
  process?: (e: RequestInstance) => void;
  handleReSend?: (e: RequestInstance) => void;
  uploadProcess?: (e: RequestInstance) => void;
  reSendRequest?: () => void;
  uploadSize?: number;
  size?: number;
  state?:
    | "ok"
    | "fail"
    | "process"
    | "init"
    | "cancel"
    | "uploadProcess"
    | "reSend";
  request?: XMLHttpRequest;
  constructor({
    process,
    uploadProcess,
    error,
    ok,
    cancel,
    handleReSend,
    timeout = 5000,
  }: {
    process?: (e: RequestInstance) => void;
    uploadProcess?: (e: RequestInstance) => void;
    error?: (e: RequestInstance) => void;
    ok?: (e: RequestInstance) => void;
    cancel?: (e: RequestInstance) => void;
    handleReSend?: (e: RequestInstance) => void;
    timeout?: number;
  }) {
    this.uploadSize = 0;
    this.size = 0;
    this.state = "init"; //ok 上传完成 fail 上传失败 process 正在上传 init 还没开始上传 cancel 取消上传 uploadProcess 请求下载过程 reSend 重发
    this.error = error;
    this.ok = ok;
    this.cancel = cancel;
    this.uploadProcess = uploadProcess;
    this.process = process;
    this.handleReSend = handleReSend;
    this.timeout = timeout;
    this.init();
  }
  init() {
    this.http = new XMLHttpRequest();
    this.http.timeout = this.timeout;
    this.http.onloadstart = () => {
      // 刚接收到数据
    };
    this.http.onabort = () => {
      // 请求关闭
    };
    this.http.onloadend = () => {
      // 请求结束
    };
    this.state = "init";
    this.http.onerror = (): void => this.handleError(); //请求失败
    this.http.upload.onprogress = (e): void => {
      // 上传过程
      this.uploadSize = Number(((e.loaded / e.total) * 100).toFixed(0));
      this.state = "uploadProcess";
      this.uploadProcess?.(this);
    };
    this.http.onprogress = (e): void => {
      // 请求下载过程
      this.size = Number(((e.loaded / e.total) * 100).toFixed(0));
      this.state = "process";
      this.process?.(this);
    };
  }
  handleError() {
    this.state = "fail";
    this.error?.(this);
  }
  // 上传文件
  public upload({
    url,
    file,
    async = true,
  }: {
    url: string;
    file: FormData;
    async?: boolean;
  }): Promise<any> {
    return new Promise((resolve, reject) => {
      // @ts-ignore
      this.http.onload = ({ target }: { target: XMLHttpRequest }) => {
        try {
          resolve(JSON.parse(target.response));
          this.state = "ok";
          this.ok?.(this);
        } catch (err) {
          reject(target.response);
          this.handleError();
        }
      }; // 请求成功

      this.reSendRequest = () => {
        this.http?.open("POST", url, async);
        this.http?.send(file);
      };
      this.reSendRequest?.();
    });
  }
  // 发送请求
  public fetch({
    method,
    url,
    body,
    header,
    async = true,
  }: {
    method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
    url: string;
    body?: { [key: string]: any };
    header?: { [key: string]: string };
    async?: boolean;
  }): Promise<any> {
    return new Promise((resolve, reject) => {
      // @ts-ignore
      this.http.onload = ({ target }: { target: XMLHttpRequest }) => {
        try {
          resolve(JSON.parse(target.response));
          this.state = "ok";
          this.ok?.(this);
        } catch (err) {
          reject(target.response);
          this.handleError();
        }
      }; // 请求成功
      if (method === ("POST" || "PUT" || "PATCH")) {
        this.http?.setRequestHeader(
          // 设置setRequestHeader必须在open后send前
          "Content-Type",
          "application/x-www-form-urlencoded"
        );
      }
      if (header)
        // 设置请求头
        Object.keys(header).forEach((item) => {
          this.http?.setRequestHeader(item, header[item]);
        });
      this.reSendRequest = () => {
        this.http?.open(method, url, async);
        this.http?.send(
          method === "POST" || "PUT" || "PATCH"
            ? body
              ? Object.keys(body).reduce(
                  (total, item) => `${total}${item}=${body[item]}&`,
                  ""
                )
              : ""
            : JSON.stringify(body)
        );
      };
      this.reSendRequest?.();
    });
  }
  public about() {
    // 取消请求
    this.http?.abort();
    this.state = "cancel";
    this.cancel?.(this);
  }
  public reSend() {
    // 重发请求
    this.http?.abort();
    this.state = "reSend";
    this.reSendRequest?.();
    this.handleReSend?.(this);
  }
}
